/******************************************************************************

	COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2010.

	This file is part of Maxsi Engine.

	Maxsi Engine is free software: you can redistribute it and/or modify it
	under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or (at your
	option) any later version.

	Maxsi engine is distributed in the hope that it will be useful, but WITHOUT
	ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
	FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
	for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with Maxsi Engine. If not, see <http://www.gnu.org/licenses/>.

	MaxsiEngine.dll
	A general purpose C++ library for the Maxsi Technology Project.

	MaxsiFileFormat.cpp
	A library for handling .mff files, a simple (compressed) achive format
	allowing people to store huge files and simple metadata. It's ridiculously
	simple to parse and could be easily extended, in fact, we urged you to,
	just send back your improvements and we'll add support.

******************************************************************************/

#include "MaxsiEngine.h"

#if !defined(ME_MFFDisabled) || defined(ME_MFFEnabled)

BeginMaxsiNamespace

//=============================================================================
//	MFFINFO
//=============================================================================

MFFINFO::MFFINFO()
{
	FileFlags	|=	MaxsiMFF_Flag_NotInitialized;
}

MFFINFO::~MFFINFO()
{
}

//=============================================================================
//	MaxsiFileFormatDevice
//=============================================================================

MaxsiFileFormatDevice::MaxsiFileFormatDevice()
{
	// Nothing to do here.
}

MaxsiFileFormatDevice::~MaxsiFileFormatDevice()
{
	// There is also nothing to do here at the moment.
}

//=============================================================================
//	MaxsiFileFormatDeviceBuffer
//=============================================================================

MaxsiFileFormatDeviceBuffer::MaxsiFileFormatDeviceBuffer()
{
	Data		=	NULL;
	DataSize	=	NULL;
	Flags		=	0;
}

MaxsiFileFormatDeviceBuffer::~MaxsiFileFormatDeviceBuffer()
{
	// Nothing to do here.
}

int MaxsiFileFormatDeviceBuffer::SetInput(BYTE** Pointer, size_t* Size)
{
	Data		=	Pointer;
	DataSize	=	Size;
}

int MaxsiFileFormatDeviceBuffer::Seek(FileSize_t Offset)
{
	if ( Offset > SIZE_MAX ) { return MaxsiDevice_EAddressSpaceTooSmall; } // We can't seek beyond 2^32 on 32-bit systems, for instance.
	
	Position = Offset;
}

int MaxsiFileFormatDeviceBuffer::Read(BYTE* Buffer, size_t BufferLen)
{
	if ( BufferLen == 0 ) { return MaxsiDevice_ESuccess; }
	if ( Buffer == NULL ) { return MaxsiDevice_EInvalidParameters; }
	if ( Data == NULL ) { return MaxsiDevice_ENotInitialized; }
	if ( *Data == NULL ) { return MaxsiDevice_ENotInitialized; }
	if ( Position+BufferLen > SIZE_MAX ) { return MaxsiDevice_EAddressSpaceTooSmall; } // Cannot seek past 2^32 on 32-bit systems
	if ( *DataSize < Position+BufferLen ) { return MaxsiDevice_ENotEnoughDataAvailable; }

	memcpy(Buffer, (*Data)+(size_t)Position, *DataSize);

	return MaxsiDevice_ESuccess;
}

int MaxsiFileFormatDeviceBuffer::Write(BYTE* Buffer, size_t BufferLen)
{
	if ( BufferLen == 0 ) { return MaxsiDevice_ESuccess; }
	if ( Buffer == NULL ) { return MaxsiDevice_EInvalidParameters; }
	if ( Data == NULL ) { return MaxsiDevice_ENotInitialized; }
	if ( Flags & MaxsiDevice_FStaticBuffer ) { return MaxsiDevice_EReadOnly; }
	if ( Position+BufferLen > SIZE_MAX ) { return MaxsiDevice_EAddressSpaceTooSmall; } // Cannot seek past 2^32 on 32-bit systems
	
	// Reallocate our data buffer if we don't have enough room. This is an
	// expensive operation and should have been predicted by higher level code
	// and dealt with by allocating 'a lot of room' so that we don't need to do
	// this often.

	if ( Position+BufferLen > *DataSize ) 
	{
		int		ReallocateError		=	SetSize(Position+BufferLen);

		if ( ReallocateError != MaxsiDevice_ESuccess ) { return ReallocateError; }
	}

	memcpy((*Data)+(size_t)Position, Buffer, BufferLen);

	return MaxsiDevice_ESuccess;
}

int MaxsiFileFormatDeviceBuffer::SetSize(FileSize_t Size)
{
	if ( Data == NULL ) { return MaxsiDevice_ENotInitialized; }
	if ( DataSize == NULL ) { return MaxsiDevice_ENotInitialized; }
	if ( Flags & MaxsiDevice_FStaticBuffer ) { return MaxsiDevice_EReadOnly; }
	if ( Size > SIZE_MAX ) { return MaxsiDevice_EAddressSpaceTooSmall; } // 32-bit systems can only address 2^32 bytes.
	if ( (size_t)Size == *DataSize ) { return MaxsiDevice_ESuccess; }

	BYTE*	NewBuffer	=	new BYTE[(size_t)Size];

	if ( NewBuffer == NULL ) { return MaxsiDevice_EAllocFailed; }

	size_t Shared		=	*DataSize;

	if ( Size < *DataSize ) { Shared = (size_t)Size; }

	memcpy(NewBuffer, Data, Shared);

	delete[] *Data;

	*Data				=	NewBuffer;
	*DataSize			=	(size_t)Size;
}

FileSize_t MaxsiFileFormatDeviceBuffer::GetSize()
{
	if ( DataSize == NULL ) { return 0; }

	return *DataSize;
}

//=============================================================================
//	MaxsiFileFormat
//=============================================================================

MaxsiFileFormat::MaxsiFileFormat()
{
	InputHandler					=	NULL;
	OutputHandler					=	NULL;

	//InputDeviceFlags				=	0;
	//InputDeviceHandle				=	INVALID_HANDLE_VALUE;
	//InputDeviceHandleOffset			=	0;
	//InputDeviceHandleLength			=	0;
	//InputDeviceBuffer				=	NULL;
	//InputDeviceBufferLength			=	0;

	//OutputDeviceFlags				=	0;
	//OutputDeviceHandle				=	INVALID_HANDLE_VALUE;
	//OutputDeviceHandleOffset		=	0;
	//OutputDeviceHandleLength		=	0;
	//OutputDeviceBuffer				=	NULL;
	//OutputDeviceBufferLength		=	NULL;
	//OutputDeviceExternBuffer		=	NULL;
	//OutputDeviceExternBufferLength	=	0;
}

MaxsiFileFormat::~MaxsiFileFormat()
{

}

int MaxsiFileFormat::SetInputDevice(MaxsiFileFormatDeviceBuffer* Device)
{
	InputDevice		=	Device;
}

int MaxsiFileFormat::SetOutputDevice(MaxsiFileFormatDeviceBuffer* Device)
{
	OutputDevice	=	Device;
}

int MaxsiFileFormat::SetDevice(MaxsiFileFormatDeviceBuffer* Device)
{
	InputDevice		=	Device;
	OutputDevice	=	Device;
}

EndMaxsiNamespace

#endif
